
/**
  ******************************************************************************
  * Copyright 2021 The Microbee Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       notify_toshiba_led_i2c.c
  * @author     baiyang
  * @date       2022-1-17
  ******************************************************************************
  */

/*----------------------------------include-----------------------------------*/
#include "notify_toshiba_led_i2c.h"

#include <rtthread.h>
#include <rtdevice.h>

#if CONFIG_HAL_BOARD != HAL_BOARD_SITL_WIN

/*-----------------------------------macro------------------------------------*/
#define TOSHIBA_LED_BRIGHT  0xFF    // full brightness
#define TOSHIBA_LED_MEDIUM  0x80    // medium brightness
#define TOSHIBA_LED_DIM     0x11    // dim
#define TOSHIBA_LED_OFF     0x00    // off

#define TOSHIBA_LED_I2C_ADDR 0x55    // default I2C bus address

#define TOSHIBA_LED_PWM0    0x01    // pwm0 register
#define TOSHIBA_LED_PWM1    0x02    // pwm1 register
#define TOSHIBA_LED_PWM2    0x03    // pwm2 register
#define TOSHIBA_LED_ENABLE  0x04    // enable register

/*----------------------------------typedef-----------------------------------*/

/*---------------------------------prototype----------------------------------*/
static void bind_function(toshiba_led_i2c* toshiba_led);
static bool hw_set_rgb(rgb_led_t led, uint8_t r, uint8_t g, uint8_t b);
static bool init(notify_device_t notify_dev);
static void _timer(void *parameter);
/*----------------------------------variable----------------------------------*/
static struct rgb_led_ops toshiba_led_ops;
static struct notify_device_ops toshiba_notify_dev_ops;
/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
/**
  * @brief       
  * @param[in]   toshiba_led  
  * @param[in]   bus  
  * @param[out]  
  * @retval      
  * @note        
  */
void toshiba_led_i2c_ctor(toshiba_led_i2c* toshiba_led, uint8_t bus)
{
    rgb_led_ctor(&toshiba_led->backend_rgb, TOSHIBA_LED_OFF, TOSHIBA_LED_BRIGHT, TOSHIBA_LED_MEDIUM, TOSHIBA_LED_DIM);
    toshiba_led->_bus = bus;

    bind_function(toshiba_led);
}

notify_device_t toshiba_led_i2c_probe(uint8_t bus)
{
    toshiba_led_i2c *toshiba_led = (toshiba_led_i2c *)rt_malloc(sizeof(toshiba_led_i2c));

    if (toshiba_led) {
        rt_memset(toshiba_led, 0, sizeof(toshiba_led_i2c));
        toshiba_led_i2c_ctor(toshiba_led, bus);

        return (notify_device_t)toshiba_led;
    }

    return NULL;
}


static void bind_function(toshiba_led_i2c* toshiba_led)
{
    toshiba_led_ops = rgb_led_ops();
    toshiba_notify_dev_ops = rgb_led_notify_device_ops();

    toshiba_led_ops.hw_set_rgb = hw_set_rgb;
    toshiba_notify_dev_ops.init = init;

    toshiba_led->backend_rgb.ops = &toshiba_led_ops;
    toshiba_led->backend_rgb.notify_dev.ops = &toshiba_notify_dev_ops;
}

static bool hw_set_rgb(rgb_led_t led, uint8_t red, uint8_t green, uint8_t blue)
{
    toshiba_led_i2c* toshiba_led = (toshiba_led_i2c* )(&led->notify_dev);

    toshiba_led->rgb.r = red;
    toshiba_led->rgb.g = green;
    toshiba_led->rgb.b = blue;

    toshiba_led->_need_update = true;
    return true;
}

static bool init(notify_device_t notify_dev)
{
    toshiba_led_i2c* toshiba_led = (toshiba_led_i2c* )notify_dev;

    // first look for led on external bus
    toshiba_led->_dev = devmgr_get_i2c_device(toshiba_led->_bus, TOSHIBA_LED_I2C_ADDR);

    if (!toshiba_led->_dev) {
        return false;
    }

    devmgr_take_bus(toshiba_led->_dev);
    devmgr_set_retries(toshiba_led->_dev, 10);

    // enable the led
    bool ret = devmgr_write_register(toshiba_led->_dev, TOSHIBA_LED_ENABLE, 0x03, false);
    if (!ret) {
        devmgr_release_bus(toshiba_led->_dev);
        return false;
    }

    // update the red, green and blue values to zero
    uint8_t val[4] = { TOSHIBA_LED_PWM0, toshiba_led->backend_rgb._led_off, toshiba_led->backend_rgb._led_off, toshiba_led->backend_rgb._led_off };
    ret = devmgr_transfer(toshiba_led->_dev, val, sizeof(val), NULL, 0);

    devmgr_set_retries(toshiba_led->_dev, 1);

    if (ret) {
        // read from sensor at 50Hz
        devmgr_register_periodic_callback(toshiba_led->_dev, 20000, _timer, toshiba_led);
    }

    devmgr_release_bus(toshiba_led->_dev);
    return ret;
}

static void _timer(void *parameter)
{
    toshiba_led_i2c* toshiba_led = (toshiba_led_i2c* )parameter;

    if (!toshiba_led->_need_update) {
        return;
    }
    toshiba_led->_need_update = false;

    /* 4-bit for each color */
    uint8_t val[4] = { TOSHIBA_LED_PWM0, (uint8_t)(toshiba_led->rgb.b >> 4),
                       (uint8_t)(toshiba_led->rgb.g / 16), (uint8_t)(toshiba_led->rgb.r / 16) };

    devmgr_transfer(toshiba_led->_dev, val, sizeof(val), NULL, 0);
}
#endif
/*------------------------------------test------------------------------------*/


